#ifndef ILI9325_H
#define ILI9325_H

#include "stm32f4xx_gpio.h"
#include "bitops.h"

// Port Definition
#define CLOCK_MODULE RCC_AHB1Periph_GPIOB
#define DATAPORT GPIOB

#define RS PBO(8)
#define WR PBO(9)
#define RD PBO(10)
#define CS PBO(11)
#define RST PBO(12)

#define ILI9325_SEL() { CS = 0; }
#define ILI9325_DISEL() { CS = 1; }

typedef enum {
    FUNC_DISABLE = 0x00,
    FUNC_ENABLE = !FUNC_DISABLE
} FunctionSet;

// Driver Output Control
typedef enum {
    S1_TO_S720 = 0x00,
    S720_TO_S1 = 0x01
} SrcDrvShift;

typedef enum {
    ORDER = 0x00,
    ODD_EVEN = 0x01
} ScanMode;

typedef struct {
    SrcDrvShift SS;
    ScanMode SM;
} DrvOutCtrl;

void ILI9325SetDrvOutCtrl(const DrvOutCtrl *drvOutCtrl);

// LCD Driving Wave Control
typedef enum {
    FRAME_INVERSION = 0x00,
    LINE_INVERSION = 0x01
} InversionMode;

typedef struct {
    InversionMode BC;
    FunctionSet EOR;
} DrvWavCtrl;

void ILI9325SetDrvWavCtrl(const DrvWavCtrl *drvWavCtrl);

//  Entry Mode
typedef enum {
    UPDATED_IN_HORIZONTAL = 0x00,
    UPDATED_IN_VERTICAL = 0x01
} UpdateDirection;

typedef enum {
    HD_VD = 0x00,
    HI_VD = 0x01,
    HD_VI = 0x02,
    HI_VI = 0x03
} ACDirection;

typedef enum {
    ONE_TF = 0x00,
    TWO_TF = 0x02,
    THREE_TF = 0x03
} DataTransterFormat;

typedef struct {
    UpdateDirection AM;
    ACDirection ID;
    FunctionSet ORG;
    FunctionSet BGR;
    DataTransterFormat dtf;
} EntryMode;

void ILI9325SetEntryMode(const EntryMode *entryMode);

// Resizing Control
typedef enum {
    NO_RESIZING = 0x00,
    HALF_RESIZING = 0x01,
    FOURTH_RESIZING = 0x03
} ResizingFactor;

typedef enum {
    PIXEL_0 = 0x00,
    PIXEL_1 = 0x01,
    PIXEL_2 = 0x02,
    PIXEL_3 = 0x03
} RemainderPixels;

typedef struct {
    ResizingFactor RSZ;
    RemainderPixels RCH;
    RemainderPixels RCV;
} ResizingCtrl;

void ILI9325SetResizingCtrl(const ResizingCtrl *resizingCtrl);

// Display Control
typedef enum {
    DISPLAY_OFF = 0x00,
    NO_OUTPUT_OPERATE = 0x01,
    DISPLAY_ON = 0x03
} DisplayPanelCtrl;

typedef enum {
    VHH = 0x01,
    VHL = 0x02,
    NORMAL = 0x03
} GateDrvOutputLevel;

typedef enum {
    PARTIAL_OFF = 0x00,
    PARTIAL_ON = 0x01
} PartialDisplay;

typedef enum {
    SCAN_CYCLE_0FRAMES = 0x00,
    SCAN_CYCLE_1FRAMES = 0x01,
    SCAN_CYCLE_3FRAMES = 0x02,
    SCAN_CYCLE_5FRAMES = 0x03,
    SCAN_CYCLE_7FRAMES = 0x04,
    SCAN_CYCLE_9FRAMES = 0x05,
    SCAN_CYCLE_11FRAMES = 0x06,
    SCAN_CYCLE_13FRAMES = 0x07,
    SCAN_CYCLE_15FRAMES = 0x08,
    SCAN_CYCLE_17FRAMES = 0x09,
    SCAN_CYCLE_19FRAMES = 0x0A,
    SCAN_CYCLE_21FRAMES = 0x0B,
    SCAN_CYCLE_23FRAMES = 0x0C,
    SCAN_CYCLE_25FRAMES = 0x0D,
    SCAN_CYCLE_27FRAMES = 0x0E,
    SCAN_CYCLE_29FRAMES = 0x0F
} ScanCycleInternal;

typedef enum {
    NORMAL_SCAN = 0x00,
    INTERNAL_SCAN = 0x02
} ScanType;

typedef struct {
    // 1
    DisplayPanelCtrl D;
    FunctionSet CL; // 0: 262,144 colors 1: 8 colors
    GateDrvOutputLevel GONDTE;
    FunctionSet BASEE; // 1: base display
    PartialDisplay PTDE;

    // 2
    u16 BP;
    u16 FP;

    // 3
    ScanCycleInternal ISC;
    ScanType PTG;

    // 4
    u16 FMI;
    FunctionSet FMARKOE;
} DisplayCtrl;

void ILI9325SetDisplayCtrl(const DisplayCtrl *displayCtrl);

// Power Control
typedef enum {
    DC1_FOSC = 0x00,
    DC1_FOSC_DIV_2 = 0x01,
    DC1_FOSC_DIV_4 = 0x02,
    DC1_FOSC_DIV_8 = 0x03,
    DC1_FOSC_DIV_16 = 0x04,
    DC1_FOSC_DIV_32 = 0x05,
    DC1_FOSC_DIV_64 = 0x06,
    DC1_FOSC_HALT = 0x07
} FreqDC1;

typedef enum {
    DC2_FOSC_DIV_4 = 0x00,
    DC2_FOSC_DIV_8 = 0x01,
    DC2_FOSC_DIV_16 = 0x02,
    DC2_FOSC_DIV_32 = 0x03,
    DC2_FOSC_DIV_64 = 0x04,
    DC2_FOSC_DIV_128 = 0x05,
    DC2_FOSC_DIV_256 = 0x06,
    DC2_FOSC_HALT = 0x07
} FreqDC2;

typedef enum {
    AMPLIFY_HALT = 0x00,
    AMPLIFYRATE_2P0 = 0x01,
    AMPLIFYRATE_2P05 = 0x02,
    AMPLIFYRATE_2P10 = 0x03,
    AMPLIFYRATE_2P20 = 0x04,
    AMPLIFYRATE_2P30 = 0x05,
    AMPLIFYRATE_2P40 = 0x06,
    AMPLIFYRATE_1P60 = 0x08,
    AMPLIFYRATE_1P65 = 0x09,
    AMPLIFYRATE_1P70 = 0x0A,
    AMPLIFYRATE_1P75 = 0x0B,
    AMPLIFYRATE_1P80 = 0x0C,
    AMPLIFYRATE_1P85 = 0x0D,
    AMPLIFYRATE_1P90 = 0x0E,
    AMPLIFYRATE_1P95 = 0x0F
} Vreg1outAmplify;

typedef enum {
    VREG1OUT_0P70 = 0x00,
    VREG1OUT_0P72 = 0x01,
    VREG1OUT_0P74 = 0x02,
    VREG1OUT_0P76 = 0x03,
    VREG1OUT_0P78 = 0x04,
    VREG1OUT_0P80 = 0x05,
    VREG1OUT_0P82 = 0x06,
    VREG1OUT_0P84 = 0x07,
    VREG1OUT_0P86 = 0x08,
    VREG1OUT_0P88 = 0x09,
    VREG1OUT_0P90 = 0x0A,
    VREG1OUT_0P92 = 0x0B,
    VREG1OUT_0P94 = 0x0C,
    VREG1OUT_0P96 = 0x0D,
    VREG1OUT_0P98 = 0x0E,
    VREG1OUT_1P00 = 0x0F,
    VREG1OUT_1P02 = 0x14,
    VREG1OUT_1P04 = 0x15,
    VREG1OUT_1P06 = 0x16,
    VREG1OUT_1P08 = 0x17,
    VREG1OUT_1P10 = 0x18,
    VREG1OUT_1P12 = 0x19,
    VREG1OUT_1P14 = 0x1A,
    VREG1OUT_1P16 = 0x1B,
    VREG1OUT_1P18 = 0x1C,
    VREG1OUT_1P20 = 0x1D,
    VREG1OUT_1P22 = 0x1E,
    VREG1OUT_1P24 = 0x1F
} VComAmplify;

typedef struct {
    // 1
    FunctionSet SLP;
    FunctionSet STB;
    FunctionSet DSTB;
    u16 AP; // amplifiers 0 ~ 7
    FunctionSet SAP;
    FunctionSet APE;
    u16 BT; // factor used in the step-up circuits 0 ~ 7

    // 2
    u16 VC; // the ratio factor of Vci to generate the reference voltages Vci1 0 ~ 7;
    FreqDC1 DC0;
    FreqDC2 DC1;

    // 3
    Vreg1outAmplify VRH;
    FunctionSet VCIRE;
    FunctionSet PON;

    // 4
    VComAmplify VDV;

    // 7
    u16 VCM;
} PowerCtrl;

void ILI9325SetPowerCtrl(const PowerCtrl *powerCtrl);

typedef enum {
    DRIVE_240_LINES = 0x1D,
    DRIVE_320_LINES = 0x27,
} DriveNumberLines;

typedef struct {
    // 1
    u16 SCN;
    DriveNumberLines NL;
    FunctionSet GS;

    // 2
    FunctionSet NDL;
    FunctionSet VLE;
    FunctionSet REV;

} GateScanCtrl;

void ILI9325SetGateScanCtrl(const GateScanCtrl *gateScanCtrl);


void WriteIR(u16 index);
void WriteReg(u16 data);
void WriteIRReg(u16 index, u16 data);

#endif // ILI9325_H
